#include "encoding_tight.h"

#include <QDebug>

namespace encoding {
namespace Tight {

enum Type {
    TightCompressionBasic = 0,
    TightCompressionFill = 0x08,
    TightCompressionJPEG = 0x09,
    TightCompressionPNG = 0x0A
};
enum FilterType {

    TightFilterCopy = 0,
    TightFilterPalette = 1,
    TightFilterGradient = 2

};

const int TightMinToCompress = 12;

int calcTightBytePerPixel(s_pixelFormatStruct pixeFormat)
{
    if (24 == pixeFormat.depth && 32 == pixeFormat.bpp) {
        return 3;
    } else {
        return pixeFormat.bpp / 8;
    }
}

// ReadCompactLen 获取动态长度
uint32_t readCompactLength(LockStream &stream)
{
    uint8_t part = 0;
    stream.read((char *)&part, 1);
    uint32_t size = part & 0x7f;
    if (0 == (part & 0x80)) {
        return size;
    }
    stream.read((char *)&part, 1);

    size |= (part & 0x7f) << 7;
    if (0 == (part & 0x80)) {
        return size;
    }
    stream.read((char *)&part, 1);

    size |= (part & 0xff) << 14;
    return size;
}

int readTightPalette(LockStream &stream, int bytesPixel)
{
    uint8_t colorCount = 0;
    stream.read((char *)&colorCount, 1);

    int numColors = colorCount + 1;
    int paletteSize = numColors * bytesPixel;
    char *paletteColorBytes = new char[paletteSize];
    stream.read(paletteColorBytes, paletteSize);

    delete[] paletteColorBytes;
    return numColors;
}

std::vector<uint8_t> Encoder::readTightData(LockStream &stream, int dataSize, int decoderID)
{
    if (dataSize < TightMinToCompress) {
        char *data = new char[dataSize];
        stream.read(data, dataSize);

        delete[] data;
        return std::vector<uint8_t>();
    }

    uint32_t zlibDataLen = readCompactLength(stream);
    char *data = new char[zlibDataLen];
    stream.read(data, zlibDataLen);

    auto newData = m_zlibTool[decoderID].decompress(data, zlibDataLen);

    delete[] data;
    return newData;
}

std::vector<uint8_t> Encoder::handleTightFilters(LockStream &stream, struct FramebufferUpdateRectangle rect,
                                                 s_pixelFormatStruct pixeFormat, uint8_t compressionControl)
{
    std::vector<uint8_t> rawData;
    uint8_t FilterIdMask = 0x40;
    uint8_t filterId;
    int decoderId = (compressionControl & 0x30) >> 4;
    if (compressionControl & FilterIdMask) {
        stream.read((char *)&filterId, 1);
    }
    uint8_t bytesPixel = calcTightBytePerPixel(pixeFormat);
    int lengthCurrentBPP = bytesPixel * rect.width * rect.height;
    // qInfo() << "filterId:" << filterId << lengthCurrentBPP;
    switch (filterId) {
    case TightFilterPalette: {
        int palette = readTightPalette(stream, bytesPixel);
        int dataLength;
        if (2 == palette) {
            dataLength = rect.height * ((rect.width + 7) / 8);
        } else {
            dataLength = rect.height * rect.width;
        }
        readTightData(stream, dataLength, decoderId);
    } break;
    case TightFilterGradient:
    case TightFilterCopy: {
        rawData = readTightData(stream, lengthCurrentBPP, decoderId);
    } break;
    default: {
        qInfo() << "handleTightFilters: Bad tight filter id:" << filterId;
    } break;
    }
    return rawData;
}

std::vector<uint8_t> Encoder::readData(LockStream &stream, struct FramebufferUpdateRectangle rect, s_pixelFormatStruct pixeFormat)
{
    std::vector<uint8_t> rawData;
    uint8_t bytesPixel = calcTightBytePerPixel(pixeFormat);
    uint8_t compressionControl = 0;
    stream.read((char *)&compressionControl, 1);

    uint8_t compType = compressionControl >> 4 & 0x0f;
    // qInfo() << "compType:" << compType << compressionControl;
    switch (compType) {
    case TightCompressionFill: { // 全部为紧凑压缩
        char *data = new char[bytesPixel];
        stream.read(data, bytesPixel);

        delete[] data;
    } break;
    case TightCompressionJPEG:
    case TightCompressionPNG: { // jpeg紧凑压缩
        if (8 == pixeFormat.bpp) {
            // Tight encoding: JPEG is not supported in 8 bpp mode.
        }
        uint32_t size = readCompactLength(stream);
        char *data = new char[size];
        stream.read(data, size);
        delete[] data;
    } break;
    default: {
        if (compType > TightCompressionJPEG) {
            // Compression control byte is incorrect!
        }
        rawData = handleTightFilters(stream, rect, pixeFormat, compressionControl);
    } break;
    }
    return rawData;
}
} // namespace Tight
} // namespace encoding